package contegor.scire.structure.queries;

import java.io.Serializable;
import java.lang.reflect.Constructor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import contegor.scire.core.Id;
import contegor.scire.core.TaskId;
import contegor.scire.core.Version;
import contegor.scire.core.queries.Query;

/**
 * Implementations of {@link Replicator} will be in charge of replicating the original {@link Query}
 * they receive in theirs constructors.
 *
 * @version $Revision$
 * @author  Héctor Cirbián Pousa ($Author$)
 */
@Version("$Id$")
public class Replicator<Q extends CircularQuery<?>> implements Serializable, Cloneable {
  private static final long serialVersionUID = -7065466163326898359L;
  private static final Logger logger = LoggerFactory.getLogger(Replicator.class);

  private transient Constructor<Q> constructor;
  private String className;
 
  public Replicator(Class<Q> clazz) {
    try {
      this.constructor = clazz.getConstructor();
      this.className   = clazz.getName();
    }
    catch (Exception ex) {
      logger.error(ex.getMessage(), ex);
    }
  }

  private Replicator(Constructor<Q> constructor) {
    this.constructor = constructor;
    this.className   = constructor.getDeclaringClass().getName();
  }

  @SuppressWarnings("unchecked")
  private Constructor<Q> getConstructor() {
    try {
      if (this.constructor == null)
        this.constructor = (Constructor<Q>)this.getClass().getClassLoader().loadClass(this.className).getConstructor();
    }
    catch (Exception ex) {
      logger.error(ex.getMessage(), ex);
    }
    return this.constructor;
  }

  public Q replicate(TaskId id, Id targetId) {
    try {
      Q repl = getConstructor().newInstance();
      repl.setTaskId(id);
      repl.setTargetContainerId(targetId);
      return repl;
    }
    catch (Exception ex) {
      logger.error(ex.getMessage(), ex);
      return null;
    }
  }

  @Override
  public Replicator<Q> clone() {
    return new Replicator<Q>(getConstructor());
  }
}
